//
// Created by feng on 19-4-12.
//

#include <stdexcept>

#include "SceneConfiguration.h"
#include "SceneField.h"
#include "SceneProcessor.h"
#include <algorithm/rayTracing/rayTracingDataStructure/grid/CudaGrid.cuh>
#include <algorithm/rayTracing/rayTracingDataStructure/heliostat/CudaHeliostat.cuh>
#include <algorithm/rayTracing/rayTracingDataStructure/receiver/CudaReceiver.cuh>
#include <algorithm/shared/tool/randomNumberGenerator/RandomGenerator.cuh>

#include <utils/Format.h>
#include <utils/Print.h>

using namespace solar;

// Set sunray, grids, receivers, heliostats
auto SceneProcessor::processScene(const std::shared_ptr<SceneField>& field) -> bool
{

    // 要获取随机数，就要先设置随机种子和随机数生成器
    //!!!!!!!!!!!!!!!!这两句缺失的话，会无法生成随机数!!!!!!!!!!!!!!!!!!!!!1
    cuda::RandomGenerator::initSeed();
    cuda::RandomGenerator::initCudaRandGenerator();

    return set_sunray_content(field->getSunRay()) &&
           set_grid_receiver_heliostat_content(field->getGrids(), field->getReceivers(),
                                               field->getHeliostats());
}

auto SceneProcessor::set_grid_receiver_heliostat_content(
    std::vector<std::shared_ptr<cuda::CudaGrid>>& grids,
    std::vector<std::shared_ptr<cuda::CudaReceiver>>& receivers,
    std::vector<std::shared_ptr<cuda::CudaHeliostat>>& heliostats) -> bool
{
    if (sceneConfiguration == nullptr)
    {
        throw std::runtime_error(
            "No scene configuration. Please load it first before process scene.");
    }

    // solar::println("set_grid_receiver_heliostat_content begins");
    auto num_rev = receivers.size();
    auto num_helio = heliostats.size();
    // solar::println(solar::format("num of rev: {} num of helio: {}", num_rev, num_helio));

    auto pixels_per_meter_for_receiver =
        float(1.0F / sceneConfiguration->getReceiver_pixel_length());
    float heliostat_pixel_length = sceneConfiguration->getHelio_pixel_length();

    // solar::println("start to get sun_dir");
    float3 sun_direction = sceneConfiguration->getSun_dir();
    // solar::println("start to set grid,rev,helio");

    for (const auto& grid : grids)
    {
        grid->Cinit();
        // printf("111\n");
        std::vector<cuda::CudaHeliostat*>
            heliostats_temp; // cuda的函数里貌似不可以用shared_ptr，转存一下
        for (auto& helio : heliostats)
        {
            heliostats_temp.push_back(helio.get());
        }
        // printf("222\n");
        grid->CGridHelioMatch(heliostats_temp);
        // printf("333\n");

        auto receiver = receivers[grid->getBelongingReceiverIndex()];
        receiver->stream_ = stream_;
        // printf("444\n");
        // solar::println(
        //     solar::format("pixel_per_meter_for_receiver: {}", pixels_per_meter_for_receiver));
        // printf("receiver: %d, pixel_per_meter_for_receiver: %f\n", receiver.get(),
        //        pixels_per_meter_for_receiver);
        receiver->CInit(pixels_per_meter_for_receiver);
        // printf("555\n");
        for (int i = 0; i < grid->getNumberOfHeliostats(); ++i)
        {
            int id = i + grid->getStartHeliostatIndex();
            float3 focus_center = receiver->getFocusCenter(
                heliostats[id]->getPosition()); // 光线从helio要打到receiver上的点
            heliostats[id]->setPixelLength(heliostat_pixel_length);
            heliostats[id]->CSetNormalAndRotate(focus_center, sun_direction);
        }
        // printf("666\n");
    }
    // solar::println(solar::format("set_grid_receiver_heliostat_content ends"));

    return true;
}

auto SceneProcessor::set_sunray_content(const std::shared_ptr<solar::cuda::CudaSunray>& sunray)
    -> bool
{
    if (sceneConfiguration == nullptr)
    {
        throw std::runtime_error(
            "No scene configuration. Please load it first before process scene.");
    }
    // solar::println("set_sunray_content");

    /**
     * sunray在RayTracingProcessor中的setSunray()函数中设置过了，这里不需要再设置
     */

    // sunray.setSunDirection(sceneConfiguration->getSun_dir());
    // sunray.setCSR(sceneConfiguration->corr_Csr());
    // sunray.setDNI(sceneConfiguration->getDni());
    // sunray.setNumOfSunshapeGroups(sceneConfiguration->getNum_sunshape_groups());
    // sunray.setNumOfSunshapeLightsPerGroup(sceneConfiguration->getNum_sunshape_lights_per_group());
    // sunray.setReflectiveRate(sceneConfiguration->getReflected_rate());

    // 生成法向扰动和光线采样的数组
    return set_perturbation(sunray.get()) && set_samplelights(sunray.get());
}

auto SceneProcessor::getSceneConfiguration() const -> SceneConfiguration*
{
    return sceneConfiguration;
}

void SceneProcessor::setSceneConfiguration(SceneConfiguration* config)
{
    this->sceneConfiguration = config;
}
